/*
 * yabase64.c
 * KJ's simplified stream archiver (kjssar)
 * 
 * (C)Copyright 2013-2015 by Klaus-J. Wolf, kj (at) seismic (dot) de
 * This file is licensed under the General Public License Version 2.
 * http://www.gnu.org/licenses/gpl-2.0.html
 * 
 * This is a proof-of-concept implementation.
 * 
 */

#include <string.h>

#include "prelim.h"
#include "yabase64.h"


const char *base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


#define WHITESPACE 64
#define EQUALS     65
#define INVALID    66

#define UNINITIALIZED 67


static char base64chars_rev[128] = {UNINITIALIZED};



int base64encode(const uchar *data_buf, int datasz, uchar *res, int ressz)
{
    const uint8 *data = (const uint8 *)data_buf;
    int resix = 0;
    int x;
    uint32 n = 0;
    int padc = datasz % 3;
    uint8 n0, n1, n2, n3;
    
    for(x=0; x<datasz; x+=3) 
    {
        n = ((uint32)data[x]) << 16;
        
        if((x+1)<datasz)
            n += ((uint32)data[x+1]) << 8;
            
        if((x+2)<datasz)
            n += data[x+2];
            
        n0 = (uint8)(n >> 18) & 0x3f;
        n1 = (uint8)(n >> 12) & 0x3f;
        n2 = (uint8)(n >> 6) & 0x3f;
        n3 = (uint8)n & 0x3f;
        
        if(resix>=ressz)
            return 1;
        
        res[resix++] = base64chars[n0];
        
        if(resix>=ressz)
            return 1;
        
        res[resix++] = base64chars[n1];
        
        if((x+1)<datasz)
        {
            if(resix>=ressz)
                return 1;
            res[resix++] = base64chars[n2];
        }
        
        if((x+2)<datasz)
        {
            if(resix>=ressz)
                return 1;
            res[resix++] = base64chars[n3];
        }
    }  
    
    if(padc>0)
    { 
        for (; padc<3; padc++) 
        { 
            if(resix>=ressz)
                return 1;
            res[resix++] = '=';
        } 
    }
    if(resix>=ressz)
        return 1;
    res[resix] = 0;
    return 0;
}


int base64decode (char *in, int insz, unsigned char *out, int outsz, int *outp)
{
    int i,o;
    int buf=1;
 
    if(base64chars_rev[0]==UNINITIALIZED)   /* this is executed just once */
    {
        memset(base64chars_rev, INVALID, sizeof base64chars_rev);
        for(i=0; base64chars[i]; i++)
            base64chars_rev[(int)base64chars[i]]=i;
        
        base64chars_rev[9]=WHITESPACE;
        base64chars_rev[32]=WHITESPACE;
        base64chars_rev['=']=EQUALS;
    }
    
    o=0;
    for(i=0; i<insz; i++)
    {
        switch(in[i])
        {
            case WHITESPACE:
                continue;
            case INVALID:
                return 1;
            case EQUALS:
                i=insz;
                continue;
            default:
                buf = (buf << 6) | in[i];
                
                if(buf & 0x1000000)
                {
                    if((o + 3) > outsz)
                        return 1;
                    out[o++] = buf >> 16;
                    out[o++] = buf >> 8;
                    out[o++] = buf;
                    buf=1;
                }   
        }
    }
    
    if(buf & 0x40000)
    {
        if(o+2 >= outsz)
            return 1;
        out[o++] = buf >> 10;
        out[o++] = buf >> 2;
    }
    else
        if(buf & 0x1000) 
        {
            if(o+1 > outsz)
                return 1;
            out[o++] = buf >> 4;
        }
    
    *outp=o;
    return 0;
}
